package com.culture.config.core;

import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import com.culture.config.common.util.JSONUtil;
import com.culture.config.service.AuthService;
import com.culture.config.service.EventLogService;
import com.culture.config.util.AuthenticationUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {	
	@Autowired
	private AuthService authService;
	@Autowired
	private EventLogService eventLogService;
	
	@Bean
	@Override
	public UserDetailsService userDetailsService() {
		return this.authService;
	}
	
	@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(this.authService);
	}		

	@Bean
	public static BCryptPasswordEncoder  passwordEncoder() {
	    return new BCryptPasswordEncoder();
	}
	
	/**
	 * get PermissionEvaluator
	 * @return PermissionEvaluator instance
	 */
	@Bean
	public PermissionEvaluator getPermissionEvaluator() {
		return new PermissionEvaluator() {
			@Override
			public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {				
				log.info("hasPermission:{},{}",targetDomainObject, permission);				
				return hasPermission(authentication,permission);
			}

			@Override
			public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
					Object permission) {
				log.info("hasPermission:{},{},{}",targetId,targetType,permission);
				return hasPermission(authentication,permission);
			}	
			
			private boolean hasPermission(Authentication authentication, Object permission) {
				Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
				for (GrantedAuthority authority : authorities) {					
					if (authority.getAuthority().equals(permission)) {
						return true;
					}
				}
				return false;
			}
		};
	}	
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring()		
		.antMatchers("/auth/stylesheets/**")		
		.antMatchers("/auth/javasccript/**");		
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {				
		http.authorizeRequests()
			.antMatchers("/auth/login.html","/auth/error").permitAll()
			.anyRequest().authenticated()
			.and()
			.formLogin().loginPage("/auth/login.html")
			.loginProcessingUrl("/login")
			.failureUrl("/auth/error")
			.successHandler(new WebAuthenticationSuccessHandler()).permitAll()
			.and()
			.httpBasic()
			.and()
			.headers().frameOptions().sameOrigin()
			.and()
            .csrf().disable();
	}	
	
	/**
	 * Web Authentication SuccessHandler
	 * @author enjoy
	 * @version 1.0
	 */
	class WebAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
		@Override
		public void onAuthenticationSuccess(HttpServletRequest request,
				HttpServletResponse response, Authentication authentication) throws ServletException, IOException {		
			eventLogService.addEventLog("onAuthenticationSuccess", JSONUtil.beanToJSONString(authentication));
			String crsfToken = AuthenticationUtil.saveCrsfToken(request);
			String redirectUrl = StringUtils.join("./auth/default.html?",AuthenticationUtil.CRSF_TOKEN_NAME,"=",crsfToken);
			response.sendRedirect(redirectUrl);
		}
	}
}
